use super::{
    err, err_value, json, ok, throw_err, Base, Client, ECode, MapSv, Resp, TBase, VBase, VMap,
    Value,
};
use base::tbase::log_stock;
use reqwest::header::{HeaderMap, HeaderName, HeaderValue, CONTENT_TYPE};
use tracing::{instrument, warn};
use uuid::Uuid;

pub struct Out;

impl Base for Out {}
impl TBase for Out {}

impl Out {
    pub async fn do_cmd(&self, para: &Value, cmd: &str, client: &Client) -> Resp {
        let data = para.ckobj()?;
        let line = data.ck_str("line")?;

        match cmd {
            "goods" => self.do_goods(line, data, client).await,
            "check" => self.do_check(line, data, client).await,
            _ => throw_err!(ECode::NoServe, "no out serve"),
        }
    }

    #[instrument(skip_all, name = "do_check")]
    async fn do_check(&self, line: &str, data: &MapSv, client: &Client) -> Resp {
        let sn = data.ck_str("sn")?;
        let end = data.ck_str("end")?;
        let tp = data.ck_str("tp")?;
        let uname = data.ck_str("uname")?;
        let num = data.ck_i64("dnum")?;

        let cmd = json!({ "line": line, "sn": sn });
        let out_body = client.cfgdb(&cmd, &format!("file/get/out{tp}")).await?;
        let out_body = out_body.ck_obj("data")?;

        match tp {
            "lathe" | "forge" => {
                let supply = out_body.ck_str("supply")?;
                let old = out_body.ck_i64("dnum")?;
                let c = num - old;

                if c <= 0 {
                    throw_err!(ECode::Para, "num error");
                }

                let mut tmp: Vec<&str> = sn.split("-").collect();

                if let None = tmp.pop() {
                    throw_err!(ECode::Para, "sn error");
                }

                let stp = match tmp.pop() {
                    Some(v) => v,
                    None => throw_err!(ECode::Para, "sn error"),
                };

                let set_sn = match tp {
                    "lathe" => {
                        if let None = tmp.pop() {
                            throw_err!(ECode::Para, "sn error");
                        }

                        let tmp_sn = tmp.join("-");

                        let ball = out_body.ck_str("ball")?;

                        format!("{tmp_sn}-{ball}")
                    }
                    "forge" => tmp.join("-").to_string(),
                    _ => throw_err!(ECode::Para, "tp error"),
                };

                let cmd = json!({ "line": line, "sn": set_sn });
                let body_set = client.cfgdb(&cmd, "file/get/outset").await?;
                let body_set = body_set.ck_obj("data")?;

                let std = body_set.ck_i64("std")?.to_string();

                let n = if stp == "01" || stp == "1" {
                    body_set.ck_f64("outnum")?
                } else if stp == "02" || stp == "2" {
                    body_set.ck_f64("inum")?
                } else {
                    throw_err!(ECode::Para, "sn error");
                };

                let b_sn = format!("{std}-{supply}");

                let cmd = json!({ "line": line, "sn": b_sn });
                let body_outblank = client.cfgdb(&cmd, "file/get/outblank").await?;
                let body_outblank = body_outblank.ck_obj("data")?;

                let dnum = body_outblank.ck_f64("dnum")?;
                let count = (n * c as f64) / 1000.0;

                let dnum = dnum - count;

                if dnum < 0.0 {
                    let cmd = json!({ "line": line, "sn": b_sn });
                    client.cfgdb(&cmd, "file/rm/outblank").await?;
                } else {
                    let cmd = json!({
                        "line": line,
                        "sn": b_sn,
                        "v": {
                            "dnum": dnum,
                            "end": end,
                            "uname": uname,
                        }
                    });

                    client.cfgdb(&cmd, "file/md/outblank").await?;
                }
            }
            "blank" => {}
            _ => throw_err!(ECode::Para, "tp error"),
        }

        let cmd = json!({
            "line": line,
            "sn": sn,
            "v": {
                "dnum": num,
                "end": end,
                "uname": uname,
            }
        });

        client.cfgdb(&cmd, &format!("file/md/out{tp}")).await?;

        let mut body = out_body.clone();
        body.insert("num".to_string(), json!(num));

        let _ = log_stock(
            line,
            &format!("lgout{tp}"),
            sn,
            "pd",
            &body,
            None,
            client,
            "",
        )
        .await;

        ok!()
    }

    #[instrument(skip_all, name = "do_goods")]
    async fn do_goods(&self, line: &str, data: &MapSv, client: &Client) -> Resp {
        let sn = data.ck_str("sn")?;

        let cmd = json!({
            "line": line,
            "sn": sn,
        });

        let body = client.cfgdb(&cmd, &format!("file/get/gorder")).await?;
        let body = body.ck_obj("data")?.ck_array("v")?;

        let mut head = HeaderMap::new();
        head.insert(CONTENT_TYPE, self.to_head("application/json")?);

        let cmd = json!({
            "comp_no": "AJZC",
            "user_id": "admin",
            "password": "",
        });

        let data = cmd.to_string();
        let url = "http://192.168.50.91/WMS/API/SYS/LOGIN";

        let ret = client.send_http(&data, url, 30, head).await?;
        warn!("login: {:?}", &ret);

        let code = ret.ck_i64("code")?;

        let mut head = HeaderMap::new();
        head.insert(CONTENT_TYPE, self.to_head("application/json")?);

        if code == 0 {
            let token = ret.ck_str("token")?;
            head.insert(
                HeaderName::from_static("authorization"),
                self.to_head(token)?,
            );
        } else {
            return ok!(json!({
                "err": ret.ck_str("message")?
            }));
        }

        let id = format!("CK{}", self.cur_time("%Y%m%d%H%M%S"));
        let cur = self.cur_time("%Y-%m-%d");
        let cur_time = self.cur_time("%Y-%m-%d %H:%M:%S");

        let mut val = Vec::new();
        for d in body.iter() {
            let d = d.ckstr()?;

            val.push(json!({
                "item_code": d,
                "qty": 1,
                "request_id": "CK",
                "request_code": id,
                "request_itm": "1",
            }));
        }

        let guid = Uuid::new_v4();

        let cmd = json!({
            "delivery_order": {
                "oth_task_id": guid.to_string(),
                "ref_id": "MES",
                "delivery_order_date": cur,
                "request_code": id,
                "warehouse_code": "002",
                "owner_code": "OWNER",
                "order_type": "01",
                "schedule_date": cur,
                "department": "00000000",
                "operator_code": "ADMIN",
                "operate_time": cur_time,
            },
            "order_lines": val,
        });

        let data = cmd.to_string();
        let url = "http://192.168.50.91/WMS/API/SYS/CreateDeliveryOrder";

        let ret = client.send_http(&data, url, 10000, head).await?;
        let code = ret.ck_i64("code")?;

        warn!("cmd: {:?}", &cmd);
        warn!("order: {:?}", &ret);

        if code != 0 {
            return ok!(json!({
                "err": ret.ck_str("message")?
            }));
        }

        ok!()
    }

    fn to_head(&self, val: &str) -> Result<HeaderValue, Value> {
        HeaderValue::from_str(val).or_else(|e| err!(ECode::Post, e))
    }
}
